<?php

namespace App\Traits;

use Illuminate\Support\Facades\Log;

/**
 * Rsa 加密处理 Trait
 *
 * @author WangBin
 * @date 2021/8/18
 */
trait RsaUtil
{
    /**
     * 解析密钥
     *
     * @param string $keyPath
     * @param array $data
     * @return \Psr\Http\Message\ResponseInterface|null
     * @throws \GuzzleHttp\Exception\GuzzleException
     * @author WangBin
     * @date 2021/8/18
     */
    public function getSmsRsaSign(array $data)
    {
        $priKey = file_get_contents(config('easysms.pfx_path', ''));
        $res = openssl_get_privatekey($priKey);
        $signStr = $this->getSign($data);
        //调用openssl内置签名方法，生成签名$sign
        openssl_sign($signStr, $sign, $res);
        //释放资源
        openssl_free_key($res);
        //base64编码
        $sign = base64_encode($sign);
        return $sign;
    }

    function getSign($data)
    {
        ksort($data, SORT_STRING);
        $str = "";
        foreach ($data as $key => $value) {
            if(is_array($value)){
                $str .= $key."=".json_encode($value)."&";
            }else{
                if($value =="null" || $value == ""){
                    unset($data[$key]);
                }else{
                    $str .= $key."=".$value."&";
                }
            }
        }
        $str = substr($str,0,-1);
        return $str;
    }

    /**
     * 根据公钥文件路径，签名，加签数据校验密钥
     *
     * @param string $publicKeyPath
     * @param string $signStr
     * @param array $data
     * @return bool
     */
    public function verifySign(string $publicKeyPath, string $signStr, array $data)
    {
        $pubKey = file_get_contents($publicKeyPath);
        $publicKeyId = openssl_pkey_get_public($pubKey);
        Log::info("验签数据,公钥路径：".$publicKeyPath);
        Log::info("验签数据排序后".$this->getSign($data));
        Log::info("验签数据【签名sign】:".$signStr);
        $result = openssl_verify($this->getSign($data), base64_decode($signStr), $publicKeyId, OPENSSL_ALGO_SHA1);
        openssl_free_key($publicKeyId);
        Log::info("验签数据，验签结果：".$result);
        return $result;
    }



    /**
     * 根据私钥文件路径，签名数据生产密钥
     *
     * @param string $privateKeyPath
     * @param array $data
     * @return string
     */
    public function generateSign(string $privateKeyPath, array $data)
    {
        $priKey = file_get_contents($privateKeyPath);
        $res = openssl_get_privatekey($priKey);
        $signStr = $this->getSign($data);
        Log::debug("[签名串]打印 signStr：{$signStr}");
        //调用openssl内置签名方法，生成签名$sign
        openssl_sign($signStr, $sign, $res);
        //释放资源
        openssl_free_key($res);
        //base64编码
        return base64_encode($sign);
    }

    /**
     * 公钥加密
     * @param string $publicKey
     * @param array $data
     * @return mixed
     * @throws \Exception
     */
    public function encryptPublic(string $publicKey,array $data)
    {
        $pKey = openssl_pkey_get_public(file_get_contents($publicKey));
        //  待加密数据转json字符串并base64
        $str = base64_encode(json_encode($data));
        $encryptedDataArray = [];
        //  分段截取加密（2048公钥加密最长字符串有240多个字符）
        foreach (str_split($str,240) as $val){
            //  加密
            $flg = openssl_public_encrypt($val,$encryptedData,$pKey,OPENSSL_PKCS1_PADDING);
            if(!$flg)
                throw new \Exception("加密失败".$val);
            //  加密数据放入到一个数组中
            $encryptedDataArray[] = base64_encode($encryptedData);
        }
        openssl_free_key($pKey);
        return $encryptedDataArray;
    }

    /**
     * 私钥解密
     * @param string $privateKey
     * @param array $encryptedDataArray
     * @return mixed
     * @throws \Exception
     */
    public function decryptPrivate(string $privateKey,array $encryptedDataArray)
    {
        $priKey = openssl_get_privatekey(file_get_contents($privateKey));
        $decryptedStr = '';
        foreach ($encryptedDataArray as $val){
            if(empty($val))
                continue;
            $flg = openssl_private_decrypt(base64_decode($val),$decryptedData,$priKey,OPENSSL_PKCS1_PADDING);
            if(!$flg)
                throw new \Exception("解密失败");
            $decryptedStr .= $decryptedData;
        }
        openssl_free_key($priKey);
        return json_decode(base64_decode($decryptedStr),true);
    }
}
